package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.exception.ServiceException;
import com.example.common.util.TreeUtils;
import com.example.domain.dto.MenuDTO;
import com.example.domain.entity.Menu;
import com.example.domain.entity.User;
import com.example.mapper.MenuMapper;
import com.example.mapper.UserMapper;
import com.example.service.MenuService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    private final UserMapper userMapper;

    @Override
    public List<Menu> getMenuList(com.example.domain.query.MenuQuery query) {
        return lambdaQuery()
                .like(StringUtils.hasText(query.getKeyword()), Menu::getTitle, query.getKeyword())
                .eq(StringUtils.hasText(query.getStatus()), Menu::getStatus, query.getStatus())
                .orderByAsc(Menu::getSort)
                .list();
    }

    @Override
    public List<Menu> getMenuTree() {
        List<Menu> menus = baseMapper.selectList(new LambdaQueryWrapper<Menu>()
                .eq(Menu::getStatus, "1")
                .eq(Menu::getDeleted, 0)
                .orderByAsc(Menu::getSort));
        return TreeUtils.buildTree(menus);
    }

    @Override
    public Menu getMenuById(Long id) {
        return getById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addMenu(MenuDTO menuDTO) {
        // 检查上级菜单是否存在
        if (menuDTO.getParentId() != 0) {
            Menu parentMenu = getById(menuDTO.getParentId());
            if (parentMenu == null) {
                throw new ServiceException("上级菜单不存在");
            }
        }

        // 检查路径是否重复
        if (StringUtils.hasText(menuDTO.getPath())) {
            boolean exists = lambdaQuery()
                    .eq(Menu::getParentId, menuDTO.getParentId())
                    .eq(Menu::getPath, menuDTO.getPath())
                    .exists();
            if (exists) {
                throw new ServiceException("同级菜单下路由路径已存在");
            }
        }

        Menu menu = new Menu();
        BeanUtils.copyProperties(menuDTO, menu);
        
        // 设置默认值
        if (menu.getSort() == null) {
            menu.setSort(0);
        }
        if (!StringUtils.hasText(menu.getStatus())) {
            menu.setStatus("1");
        }

        // 保存菜单
        save(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(MenuDTO menuDTO) {
        Menu menu = getById(menuDTO.getId());
        if (menu == null) {
            throw new ServiceException("菜单不存在");
        }

        // 不能将自己设为自己的父菜单
        if (menuDTO.getId().equals(menuDTO.getParentId())) {
            throw new ServiceException("不能将菜单设为自己的子菜单");
        }

        // 不能将自己设为自己子菜单的父菜单
        if (isChild(menuDTO.getId(), menuDTO.getParentId())) {
            throw new ServiceException("不能将菜单设为它的子菜单的父菜单");
        }

        BeanUtils.copyProperties(menuDTO, menu);
        updateById(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenu(Long id) {
        // 检查是否有子菜单
        if (hasChildren(id)) {
            throw new ServiceException("存在子菜单，无法删除");
        }

        // 检查是否被角色使用
        if (isUsedByRole(id)) {
            throw new ServiceException("菜单已被角色使用，无法删除");
        }

        removeById(id);
    }

    @Override
    public List<Menu> getUserMenus() {
        // 获取当前用户ID
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = userMapper.selectByUsername(username);

        // 获取用户菜单列表（只获取目录和菜单，不包括按钮）
        List<Menu> menus = baseMapper.selectMenusByUserId(user.getId());

        // 构建菜单树
        return buildTree(menus);
    }

    @Override
    public List<String> getUserPermissions() {
        // 获取当前用户ID
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        User user = userMapper.selectByUsername(username);

        // 获取用户权限列表
        return baseMapper.selectPermissionsByUserId(user.getId());
    }

    /**
     * 构建菜单树
     */
    private List<Menu> buildTree(List<Menu> menus) {
        // 先按父ID分组
        Map<Long, List<Menu>> childrenMap = menus.stream()
                .filter(menu -> menu.getParentId() != 0)
                .collect(Collectors.groupingBy(Menu::getParentId));

        // 设置子菜单
        menus.forEach(menu -> {
            List<Menu> children = childrenMap.get(menu.getId());
            if (children != null) {
                children.sort((a, b) -> a.getSort() - b.getSort()); // 按排序升序
                menu.setChildren(children);
            }
        });

        // 返回顶级菜单
        return menus.stream()
                .filter(menu -> menu.getParentId() == 0)
                .sorted((a, b) -> a.getSort() - b.getSort()) // 按排序升序
                .collect(Collectors.toList());
    }

    /**
     * 检查是否有子菜单
     */
    private boolean hasChildren(Long menuId) {
        return lambdaQuery()
                .eq(Menu::getParentId, menuId)
                .exists();
    }

    /**
     * 检查菜单是否被角色使用
     */
    private boolean isUsedByRole(Long menuId) {
        return baseMapper.countRoleMenuByMenuId(menuId) > 0;
    }

    /**
     * 检查是否是子菜单
     */
    private boolean isChild(Long menuId, Long parentId) {
        if (menuId.equals(parentId)) {
            return true;
        }
        Menu parent = getById(parentId);
        if (parent == null || parent.getParentId() == 0) {
            return false;
        }
        return isChild(menuId, parent.getParentId());
    }
} 